/*
 * UsbController.java
 * This file is part of UsbController
 *
 * Copyright (C) 2012 - Manuel Di Cerbo
 *
 * UsbController is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * UsbController is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with UsbController. If not, see <http://www.gnu.org/licenses/>.
 */
package com.example.Util;

import static com.example.Util.DevComm.PID;
import static com.example.Util.DevComm.VID;

import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;


import java.util.HashMap;


//import android.os.

/**
 * (c) Neuxs-Computing GmbH Switzerland
 *
 * @author Manuel Di Cerbo, 02.02.2012
 */


public class UsbController {

    //    private final UsbManager mUsbManager;
    private int epInSize, mepOutSize;

    private byte[] transferBuf;
    private UsbDeviceConnection usbDeviceConnection = null;
    private UsbInterface usbInterface = null;
    private UsbEndpoint epIN = null;
    private UsbEndpoint epOUT = null;

    protected static final String ACTION_USB_PERMISSION = "com.android.usb.USB_PERMISSION";


    /**
     * Activity is needed for onResult
     */
    public UsbController() {

        transferBuf = new byte[512];

    }

    public boolean init(Context context) {
        UsbManager mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
        return enumerate(mUsbManager, d -> {
            UsbManager usbman = (UsbManager) context.getSystemService(Context.USB_SERVICE);
            PendingIntent pi = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
            context.registerReceiver(mPermissionReceiver, new IntentFilter(ACTION_USB_PERMISSION));
            usbman.requestPermission(d, pi);
        });
    }

    public boolean unInit() {
        if (usbDeviceConnection != null) {
            usbDeviceConnection.releaseInterface(usbInterface);
            usbDeviceConnection.close();
            usbDeviceConnection = null;
            return true;
        }
        return false;
        //stop();
    }
    private UsbManager usbManager;
    @SuppressLint("SuspiciousIndentation")
    private Boolean enumerate(UsbManager usbManager, IPermissionListener listener) {
        HashMap<String, UsbDevice> devList = usbManager.getDeviceList();
        this.usbManager = usbManager;
        for (UsbDevice dev : devList.values()) {
            // Todo 如何插入的是2个指纹模块，不知道怎么处理
            if (dev.getVendorId() == VID && dev.getProductId() == PID) {
                if (!usbManager.hasPermission(dev)) {
                    listener.onPermissionDenied(dev);
                    return false;
                } else {
                    usbDeviceConnection = usbManager.openDevice(dev);
                    return GetConnInterface(dev);
                }
            }
        }
        return false;
    }

    private class PermissionReceiver extends BroadcastReceiver {
        private final IPermissionListener mPermissionListener;

        public PermissionReceiver(IPermissionListener permissionListener) {
            mPermissionListener = permissionListener;
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            context.unregisterReceiver(this);
            if (intent.getAction() != null && intent.getAction().equals(ACTION_USB_PERMISSION)) {
                if (!intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    mPermissionListener.onPermissionDenied((UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE));
                } else {
                    UsbDevice dev = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                    if (dev != null) {
                        if (dev.getVendorId() == VID && dev.getProductId() == PID) {
                            usbDeviceConnection = usbManager.openDevice(dev);
                            GetConnInterface(dev);
                        }
                    }
                }
            }
        }
    }

    private boolean GetConnInterface(UsbDevice dev) {
        int n;
        n = dev.getInterfaceCount();
        if (n <= 0)
            return false;
        if (!usbDeviceConnection.claimInterface(dev.getInterface(0), true)) {
            return false;
        }
        usbInterface = dev.getInterface(0);
        n = usbInterface.getEndpointCount();
        if (n < 2) {
            return false;
        }
        for (int i = 0; i < n; i++) {
            if (usbInterface.getEndpoint(i).getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
                if (usbInterface.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN)
                    epIN = usbInterface.getEndpoint(i);
                else
                    epOUT = usbInterface.getEndpoint(i);
            }
        }

        epInSize = epIN.getMaxPacketSize();
        mepOutSize = epOUT.getMaxPacketSize();


        return true;
    }

    public boolean UsbSCSIWrite(byte[] pCDB, int nCDBLen, byte[] pData, int nDataLen, int nTimeOut) {
        LogAndToastUtils.log("Enroll: " + "UsbSCSIWrite");
        byte[] w_abyTmp = new byte[31];
        byte[] w_abyCSW = new byte[13];
        boolean w_bRet;

        //Arrays.fill(w_abyTmp, (byte)0);
        w_abyTmp[0] = 0x55;
        w_abyTmp[1] = 0x53;
        w_abyTmp[2] = 0x42;
        w_abyTmp[3] = 0x43;
        w_abyTmp[4] = 0x28;
        w_abyTmp[5] = 0x2b;
        w_abyTmp[6] = 0x18;
        w_abyTmp[7] = (byte) 0x89;
        w_abyTmp[8] = 0x00;
        w_abyTmp[9] = 0x00;
        w_abyTmp[10] = 0x00;
        w_abyTmp[11] = 0x00;
        w_abyTmp[12] = 0x00;    //cCBWFlags
        w_abyTmp[13] = 0x00;    //cCBWlun
        w_abyTmp[14] = 0x0a;    //cCBWCBLength

        System.arraycopy(pCDB, 0, w_abyTmp, 15, nCDBLen);
        //System.arraycopy(pData, 0, w_abyTmp, 31, nDataLen);

        w_bRet = UsbBulkSend(w_abyTmp, 31, nTimeOut);

        if (!w_bRet)
            return false;

        w_bRet = UsbBulkSend(pData, nDataLen, nTimeOut);

        if (!w_bRet)
            return false;

        // receive csw
        w_bRet = UsbBulkReceive(w_abyCSW, 13, nTimeOut);

        return w_bRet;
    }

    public boolean UsbSCSIRead(byte[] pCDB, int nCDBLen, byte[] pData, int nDataLen, int nTimeOut) {
        long w_nTime;
        byte[] w_abyTmp = new byte[31];
        byte[] w_abyCSW = new byte[13];
        boolean w_bRet;

        //Arrays.fill(w_abyTmp, (byte)0);
        w_abyTmp[0] = 0x55;
        w_abyTmp[1] = 0x53;
        w_abyTmp[2] = 0x42;
        w_abyTmp[3] = 0x43;
        w_abyTmp[4] = 0x28;
        w_abyTmp[5] = 0x2b;
        w_abyTmp[6] = 0x18;
        w_abyTmp[7] = (byte) 0x89;
        w_abyTmp[8] = 0x00;
        w_abyTmp[9] = 0x00;
        w_abyTmp[10] = 0x00;
        w_abyTmp[11] = 0x00;
        w_abyTmp[12] = (byte) 0x80;    //cCBWFlags
        w_abyTmp[13] = 0x00;    //cCBWlun
        w_abyTmp[14] = 0x0a;    //cCBWCBLength

        System.arraycopy(pCDB, 0, w_abyTmp, 15, nCDBLen);

        w_bRet = UsbBulkSend(w_abyTmp, 31, nTimeOut);
        if (!w_bRet) {
            return false;
        }

        w_bRet = UsbBulkReceive(pData, nDataLen, nTimeOut);

        if (!w_bRet) {
            return false;
        }
        // receive csw
        w_bRet = UsbBulkReceive(w_abyCSW, 13, nTimeOut);
        LogAndToastUtils.log("UsbSCSIRead1: " + w_bRet);
        return w_bRet;
    }

    private boolean UsbBulkSend(byte[] pBuf, int nLen, int nTimeOut) {
        int i, n, r, w_nRet;

        n = nLen / mepOutSize;
        r = nLen % mepOutSize;

        for (i = 0; i < n; i++) {
            System.arraycopy(pBuf, i * mepOutSize, transferBuf, 0, mepOutSize);

            w_nRet = usbDeviceConnection.bulkTransfer(epOUT, transferBuf, mepOutSize, nTimeOut);

            if (w_nRet != mepOutSize) {
                return false;
            }

        }

        if (r > 0) {
            System.arraycopy(pBuf, i * mepOutSize, transferBuf, 0, r);

            w_nRet = usbDeviceConnection.bulkTransfer(epOUT, transferBuf, r, nTimeOut);

            return w_nRet == r;
        }

        return true;
    }

    private boolean UsbBulkReceive(byte[] pBuf, int nLen, int nTimeOut) {
        int i, n, r, w_nRet;

        n = nLen / epInSize;
        r = nLen % epInSize;

        for (i = 0; i < n; i++) {
            w_nRet = usbDeviceConnection.bulkTransfer(epIN, transferBuf, epInSize, nTimeOut);
            if (w_nRet != epInSize) {
                return false;
            }
            System.arraycopy(transferBuf, 0, pBuf, i * epInSize, epInSize);
        }

        if (r > 0) {
            w_nRet = usbDeviceConnection.bulkTransfer(epIN, transferBuf, r, nTimeOut);
            if (w_nRet != r) {
                return false;
            }
            System.arraycopy(transferBuf, 0, pBuf, i * epInSize, r);
        }

        return true;
    }

    private final BroadcastReceiver mPermissionReceiver = new PermissionReceiver(d -> {
    });

    private interface IPermissionListener {
        void onPermissionDenied(UsbDevice d);
    }

}
